package cn.appsm.persistance.pageSupport;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.mapping.MappedStatement.Builder;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.binding.MapperMethod.ParamMap;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.asm.commons.TryCatchBlockSorter;

import cn.appsm.base.Pageable;
import cn.appsm.persistance.pageSupport.PageDialect.DBMS;

/**
 * 废弃，侵入性太大！
 * 1.硬解析
 * 2.性能？mappedStatement每次都NEW?
 * @author flatychen
 * @date 2014-6-12
 */
@Deprecated
@Intercepts(@Signature(args = { MappedStatement.class, Object.class,
		RowBounds.class, ResultHandler.class }, method = "query", type = Executor.class))
public class PageInterceptor implements Interceptor {

	// 数据库方言
	private DBMS dbms = null;

	static int MAPPED_STATEMENT_INDEX = 0;
	static int PARAMETER_INDEX = 1;
	static int ROWBOUNDS_INDEX = 2;
	static int RESULT_HANDLER_INDEX = 3;

	@SuppressWarnings("unused")
	private void process(final Object args[]) throws InvocationTargetException,
			IllegalAccessException {
		// 检查是否含有分页参数
		Pageable pageParams = this.checkIsPageable(args[PARAMETER_INDEX]);
		// 含有分页参数，进行拦截
		if (pageParams != null) {
			MappedStatement mappedStatement = (MappedStatement) args[MAPPED_STATEMENT_INDEX];
			
			// 构建分页语句
			
			int offset = pageParams.getPageNo() * pageParams.getPageSize();
			int limit = pageParams.getPageNo() * pageParams.getPageSize();
			
			Object parameter = args[PARAMETER_INDEX];
			BoundSql boundSql = mappedStatement.getBoundSql(parameter);
	//		this.addParameterMapping(parameter,mappedStatement, boundSql, Integer.class, offset);
	//		this.addParameterMapping(parameter,mappedStatement, boundSql, Integer.class, limit);
			
			Map<String,Object> mapParameter = (Map<String, Object>) parameter;
			mapParameter.put(mapParameter.size()/2+"", (pageParams.getPageNo() - 1 )*pageParams.getPageSize());
			mapParameter.put(":offset", (pageParams.getPageNo() - 1 )*pageParams.getPageSize());
			List<ParameterMapping> parameterMappings= boundSql.getParameterMappings();
			ParameterMapping.Builder builder = new ParameterMapping.Builder(mappedStatement.getConfiguration(), ":offset", Integer.class);
			parameterMappings.add(builder.build());
//			
//			
//			
//			mapParameter.put(mapParameter.size()/2+"", pageParams.getPageNo() * pageParams.getPageSize());
//			mapParameter.put(":limit", pageParams.getPageNo() * pageParams.getPageSize());
//			ParameterMapping.Builder builder2 = new ParameterMapping.Builder(mappedStatement.getConfiguration(), ":limit", Integer.class);
//			parameterMappings.add(builder2.build());
			
			
			String sql = boundSql.getSql().trim().toString();
			String pageSql = selectPageDialect(this.dbms).getPlaceHolderPageSql(sql,
					(pageParams.getPageNo() - 1 )*pageParams.getPageSize(),pageParams.getPageNo() * pageParams.getPageSize());
			args[ROWBOUNDS_INDEX] = new RowBounds(RowBounds.NO_ROW_OFFSET,
					RowBounds.NO_ROW_LIMIT);
			// 重新构建mybatis查询相关对象
			BoundSql newBoundSql = copyFromBoundSql(mappedStatement, boundSql,
					pageSql);
			//List<ParameterMapping> parameterMappings= newBoundSql.getParameterMappings();
		//	parameterMappings.add(ParameterMapping.Builder.);
			MappedStatement newMs = copyFromMappedStatement(mappedStatement,
					new BoundSqlSqlSource(newBoundSql));
			args[MAPPED_STATEMENT_INDEX] = newMs;
		}

	}
	
	private <T> void addParameterMapping(Object parameter,MappedStatement ms,BoundSql  bq, Class<T> clazz,Object v){
		int paramSize = 0;
		Map<String, Object> param = null;
		if(parameter instanceof Map){
			param = (Map<String, Object>) parameter;
			paramSize = param.size()/2;
		}else{
			param = new MapperMethod.ParamMap<Object>();
			param.put("0", v);
			param.put("param1", v); 
			paramSize = param.size()/2;
			
		}
		List<ParameterMapping> parameterMappings= bq.getParameterMappings();
		
		param.put("param"+(paramSize+1), 10);
		param.put(":offset", 10);
		ParameterMapping.Builder offsetBuilder = new ParameterMapping.Builder(ms.getConfiguration(), ":offset", Integer.class);
		
		try {
			parameterMappings.add(offsetBuilder.build());
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		param.put("param"+(paramSize+1), 20);
		param.put(":limit", 20);
		ParameterMapping.Builder limitBuilder = new ParameterMapping.Builder(ms.getConfiguration(), ":limit", Integer.class);
		parameterMappings.add(limitBuilder.build());
		
	}

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		this.process(invocation.getArgs());
		return invocation.proceed();
	}

	@Override
	public Object plugin(Object target) {
		if(dbms == null){
			throw new NullPointerException("---->PageInterceptor数据库类型（dbms）不可为空！");
		}
		if (Executor.class.isAssignableFrom(target.getClass())) {
			return Plugin.wrap(target, this);
		}
		return target;
	}

	@Override
	public void setProperties(Properties properties) {
		String dbms = properties.getProperty("dbms");
		this.setDbms(dbms);
	}

	/**
	 * 检查是否含有分页参数
	 * 
	 * @author flatychen
	 * @date 2014-6-12
	 * @param params
	 * @return
	 * @version
	 */
	private Pageable checkIsPageable(Object params) {
		if (params == null) {
			return null;
		}
		if (Pageable.class.isAssignableFrom(params.getClass())) {
			return (Pageable) params;
		} else if (params instanceof ParamMap) {
			@SuppressWarnings("unchecked")
			ParamMap<Object> paramMap = (ParamMap<Object>) params;
			for (Map.Entry<String, Object> entry : paramMap.entrySet()) {
				Object paramValue = entry.getValue();
				if (Pageable.class.isAssignableFrom(paramValue.getClass())) {
					return (Pageable) paramValue;
				}
			}
		}

		return null;
	}

	@SuppressWarnings("unused")
	private BoundSql copyFromBoundSql(MappedStatement ms, BoundSql boundSql,
			String sql) {
		BoundSql newBoundSql = new BoundSql(ms.getConfiguration(), sql,
				boundSql.getParameterMappings(), boundSql.getParameterObject());
		newBoundSql.setAdditionalParameter(":offset", 20);
		for (ParameterMapping mapping : boundSql.getParameterMappings()) {
			String prop = mapping.getProperty();
			if (boundSql.hasAdditionalParameter(prop)) {
				newBoundSql.setAdditionalParameter(prop,
						boundSql.getAdditionalParameter(prop));
			}
		}
	
		return newBoundSql;
	}

	// see: MapperBuilderAssistant
	@SuppressWarnings("unused")
	private MappedStatement copyFromMappedStatement(MappedStatement ms,
			SqlSource newSqlSource) {
		Builder builder = new MappedStatement.Builder(ms.getConfiguration(),
				ms.getId(), newSqlSource, ms.getSqlCommandType());

		builder.resource(ms.getResource());
		builder.fetchSize(ms.getFetchSize());
		builder.statementType(ms.getStatementType());
		builder.keyGenerator(ms.getKeyGenerator());
		if (ms.getKeyProperties() != null && ms.getKeyProperties().length != 0) {
			StringBuilder keyProperties = new StringBuilder();
			for (String keyProperty : ms.getKeyProperties()) {
				keyProperties.append(keyProperty).append(",");
			}
			keyProperties.delete(keyProperties.length() - 1,
					keyProperties.length());
			builder.keyProperty(keyProperties.toString());
		}

		// setStatementTimeout()
		builder.timeout(ms.getTimeout());

		// setStatementResultMap()
		builder.parameterMap(ms.getParameterMap());

		// setStatementResultMap()
		builder.resultMaps(ms.getResultMaps());
		builder.resultSetType(ms.getResultSetType());

		// setStatementCache()
		builder.cache(ms.getCache());
		builder.flushCacheRequired(ms.isFlushCacheRequired());
		builder.useCache(ms.isUseCache());

		return builder.build();
	}

	private PageDialect selectPageDialect(DBMS dbms) {
		switch (dbms) {
		case MYSQL:
			return new MysqlPageDialect();
		case ORACLE:
			return new OraclePageDialect();
		default:
			throw new UnsupportedOperationException("---->不支持的数据库！");
		}
	}

	public static class BoundSqlSqlSource implements SqlSource {
		BoundSql boundSql;

		public BoundSqlSqlSource(BoundSql boundSql) {
			this.boundSql = boundSql;
		}

		public BoundSql getBoundSql(Object parameterObject) {
			return boundSql;
		}
	}

	public void setDbms(String dbms) {
		try {
			this.dbms = PageDialect.DBMS.valueOf(dbms.toUpperCase());
		} catch (IllegalArgumentException e) {
			throw new IllegalArgumentException("---->PageInterceptor数据库类型不合法！");
		} catch (NullPointerException e) {
			throw new NullPointerException(
					"---->PageInterceptor数据库类型（dbms）不可为空！");
		}

	}

	public void addPageParameterMapping(BoundSql boundSql ,Pageable pageable){
	//	ParameterMapping.Builder
	//	boundSql.getParameterMappings().add(e);
	}
	
}
